package platformer;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


/**
 * main class for the game as a Swing application
 * @author Federico
 *
 */

public class GameMain extends JFrame {     

	/**
	 * 
	 */
	private static final long serialVersionUID = -3581233143004838926L;

	/**
	 *  Constants for the game
	 */
	static final int 	CANVAS_WIDTH 	= 96*6;   						// width and height of the game screen
	static final int 	CANVAS_HEIGHT 	= 96;
	static final int 	UPDATE_RATE 	= 10;    						// number of game update per second
	static final long 	UPDATE_PERIOD 	= 1000000000L / UPDATE_RATE;  	// nanoseconds
	static final long 	GAME_TO_DRAW_RATIO = 4;							// The game to draw loop ratio

	/**
	 *  Enumeration for the states of the game.
	 *
	 */
	static enum State {
		INITIALIZED, PLAYING, PAUSED, GAMEOVER, DESTROYED
	}

	/**
	 *  current state of the game
	 */

	static State state;  

	// Define instance variables for the game objects
	// ......
	// ......

	/**
	 *  Handle for the custom drawing panel
	 */
	private ImageCanvas canvas;

	/**
	 *  Constructor to initialize the UI components and game objects
	 */
	public GameMain() {
		/**
		 *  Initialize the game objects
		 */
		this.gameInit();

		/**
		 *  UI components
		 */
		canvas = new ImageCanvas(CANVAS_WIDTH, CANVAS_HEIGHT);
		canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
		this.setContentPane(canvas);

		// TODO Add other UI components such as button, score board, if any.
		// ......

		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.pack();
		this.setTitle("PLATFORMER");
		this.setVisible(true);

		gameStart();
	}

	//TODO All the game related codes here

	/**
	 *  Initialize all the game objects, run only once in the constructor of the main class.
	 */
	public void gameInit() {
		// ...... 
		state = State.INITIALIZED;
	}

	/**
	 *  Shutdown the game, clean up code that runs only once.
	 */
	public void gameShutdown() {
		// ...... 
	}

	/**
	 *  To start and re-start the game.
	 */
	public void gameStart() { 
		/**
		 *  Create a new thread
		 */
		Thread gameThread =  new Thread() {
			// Override run() to provide the running behavior of this thread.
			@Override
			public void run() {
				gameLoop();
			}
		};
		// Start the thread. start() calls run(), which in turn calls gameLoop().
		gameThread.start();
	}

	/**
	 *  Run the game loop here.
	 */
	private void gameLoop() {
		// Regenerate the game objects for a new game
		// ......
		state = State.PLAYING;

		// Game loop
		long beginTime, timeTaken, timeLeft;
		while (true) {
			beginTime = System.nanoTime();
			if (state == State.GAMEOVER) break;  // break the loop to finish the current play
			if (state == State.PLAYING) {
				// Update the state and position of all the game objects,
				// detect collisions and provide responses.
				gameUpdate();
			}
			// Refresh the display
			repaint();
			// Delay timer to provide the necessary delay to meet the target rate
			timeTaken = System.nanoTime() - beginTime;
			timeLeft = (UPDATE_PERIOD - timeTaken) / 1000000L;  // in milliseconds
			if (timeLeft < 10) timeLeft = 10;   // set a minimum
			try {
				// Provides the necessary delay and also yields control so that other thread can do work.
				Thread.sleep(timeLeft);
			} catch (InterruptedException ex) { }
		}
	}

	/**
	 * Update the state and position of all the game objects,
	 * detect collisions and provide responses.
	 */
	public void gameUpdate() { 
		//TODO gameUpdate
	}

	/**
	 * Refresh the display. Called back via repaint(), which invoke the paintComponent().
	 * @param g2d
	 */
	private void gameDraw(Graphics2D g2d) {
		switch (state) {
			case INITIALIZED:
				// ......
				break;
			case PLAYING:
				// ......
				break;
			case PAUSED:
				// ......
				break;
			case GAMEOVER:
				// ......
				break;
			case DESTROYED:
				// ......
				break;
		}
		// ...... 
	}

	/**
	 *  Process a key-pressed event. Update the current state.
	 * @param keyCode
	 */
	public void gameKeyPressed(int keyCode) {
		switch (keyCode) {
			case KeyEvent.VK_UP:
				// ......
				break;
			case KeyEvent.VK_DOWN:
				// ......
				break;
			case KeyEvent.VK_LEFT:
				// ......
				break;
			case KeyEvent.VK_RIGHT:
				// ......
				break;
		}
	}

	/**
	 * Process a key-released event.
	 * @param keyCode
	 */
	public void gameKeyReleased(int keyCode) {  }

	/**
	 * Process a key-typed event.
	 * @param keyChar
	 */
	public void gameKeyTyped(char keyChar) {  }

	// Other methods
	// ......

	/**
	 * Custom drawing panel, written as an inner class.
	 *
	 */
	class GameCanvas extends JPanel implements KeyListener {
		/**
		 * 
		 */
		private static final long serialVersionUID = 3758815409815394951L;

		// Constructor
		public GameCanvas() {
			setFocusable(true);  // so that can receive key-events
			requestFocus();
			addKeyListener(this);
		}

		// Override paintComponent to do custom drawing.
		// Called back by repaint().
		@Override
		public void paintComponent(Graphics g) {
			Graphics2D g2d = (Graphics2D)g;
			super.paintComponent(g2d);   // paint background
			setBackground(Color.BLACK);  // may use an image for background
			// Draw the game objects
			gameDraw(g2d);
		}

		/**
		 *  KeyEvent handlers
		 */
		@Override
		public void keyPressed(KeyEvent e) {
			gameKeyPressed(e.getKeyCode());
		}

		@Override
		public void keyReleased(KeyEvent e) {
			gameKeyReleased(e.getKeyCode());
		}

		@Override
		public void keyTyped(KeyEvent e) {
			gameKeyTyped(e.getKeyChar());
		}
	}

	/**
	 * Main
	 * @param args
	 */
	public static void main(String[] args) {
		// Use the event dispatch thread to build the UI for thread-safety.
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				new GameMain();
			}
		});
	}
}
